#include "Game.h"
#include <vector>

Game::Game(void)
{	
}

Game::~Game(void)
{
}

void Game::loadLevel(char * filename){

	//
	//Loads information about a level
	//
	gameTimer = 0;
	StageParam *c = new StageParam(filename);
	victory = false;
	loss = false;
	tait = c->getTait();
	map = c->getMap();
	objects = c->getObjects();
	stage = c->getStage();
	maxX = c->getX();
	maxY = c->getY();
	maxZ = c->getZ();
	levelName = filename;
	status = new Status(c);
	c->~StageParam(); //dump memory

}

void Game::restartLevel(){
	//
	//restarts the current level
	//
	loadLevel(levelName);
}


void Game::drawMap(){

	// CONSOLE OUTPUT
	system("cls");
	for(int k = 0; k<maxZ;k++){
		for(int j = 0; j<maxY;j++){
			for(int i = 0; i<maxX;i++){
				cout<<map[i][j][k]->getObjectType();
			}
			cout<<endl;
		}
	}

	// OPENGL OUTPUT


}

bool Game::isVictory(){	return victory;}
bool Game::isLoss(){return loss;}
//returns location of tait (for use in render)
vector<int> Game::taitLoc(){
	vector<int> r;
	r.push_back(tait->getX());
	r.push_back(tait->getY());
	r.push_back(tait->getZ());
	r.push_back(tait->getDirection());
	r.push_back(status->getCurrentEquip());
	r.push_back(status->getCountdown());
	return r;
}

//When pulsed does actions based on the current timer pulse
void Game::updateGameTimer(){
	gameTimer++;
	if(gameTimer%10==0)
	status->removeSecond(); //decrement the levels timer

	if(gameTimer>=60){
		gameTimer=0;
	}

	executeMovements();	//execute monster movements
		
	executeFloorEffects(); //cleans up useded blocks and stuff	
	checkLoss(); //check for losses
	cleanUp(); //clean up what neds to be cleaned up
}

void Game::executeFloorEffects(){
	//
	//Does floor effects for objects
	//
	for (int i = 0; i < (int)objects.size(); i++)
	{
		objects[i]->doFloor(status->getCurrentEquip());	
		if(objects[i]->isCollide()){
			handleCollision(objects[i]); //handles collision for floor effects
		}		
		
		objects[i]->applyGravity(status->getCurrentEquip()); //apply gravity
	}
}

void Game::executeMovements() // Collision is a side-effect of movement
{
	for (int i = 0; i < (int)objects.size(); i++)
	{
		objects[i]->doTimeStep(tait);
		if(objects[i]->isCollide()){
			handleCollision(objects[i]);
		}		
	}
}



void Game::moveTait(Direction direction) //Check Victory Condition, is a side-effect of MoveTait();
{
	if(tait->getCondition()!=SLIDING){
		tait->move(direction);
		checkCollisions();
		tait->applyGravity(status->getCurrentEquip());
		checkLoss();
		cleanUp(); //cleans up useded blocks and stuff
	}
}

//Checks various possible loss conditions that would have to do with stepping on the floor
void Game::checkLoss(){

	if ((map[tait->getX()][tait->getY()][tait->getZ()]->getType() == FIRE
		&&map[tait->getX()][tait->getY()][tait->getZ()]->getFloorState()==ABLAZE
		&&status->getCurrentEquip() != FIREBOOTS)
		||
		(map[tait->getX()][tait->getY()][tait->getZ()]->getType() == WATER
		&&map[tait->getX()][tait->getY()][tait->getZ()]->getFloorState()==WATERFILLED))	
	{		
			loss = true;
	}
}

//This is used specifically for when the player makes an input otherwise collisions are handled in execute movements
void Game::checkCollisions(){
	for(int i =0; i<(int)objects.size();i++){
		if(objects[i]->isCollide()){
			handleCollision(objects[i]);
		}
	}
}

//handles collisions
void Game::handleCollision(Object* collider){
	//find the victim of the collision
	Object* collidee;
	if(collider->bounds()){
		switch(collider->getDirection()){
			case UP:
				collidee = findObject(map[collider->getX()][collider->getY()-1][collider->getZ()]);
				break;
			case DOWN:
				collidee = findObject(map[collider->getX()][collider->getY()+1][collider->getZ()]);
				break;
			case RIGHT:
				collidee = findObject(map[collider->getX()+1][collider->getY()][collider->getZ()]);
				break;
			case LEFT:
				collidee = findObject(map[collider->getX()-1][collider->getY()][collider->getZ()]);
				break;
		}
		//Checks to see if you've hit a pick up
		if(isPickUp(collider, collidee)){
			//If you've hit one do whatever logic is unique for that and update game and status
			collidePickUp(collidee);
		}

		//Checks if you're at a gate
		if(isGate(collider,collidee)){
			collideGate(collidee);
		}

		
		//Checks to see if a collision will result in a loss
		if(checkCollideLoss(collider, collidee)){
			loss=true;
		}

		//see what happens when the collidee gets hit by the collider
		collidee->collide(collider);
		if(collidee->isCollide()){
			handleCollision(collidee);
		}
		collider->collideWith(collidee);
	}
}

bool Game::checkCollideLoss(Object* collider, Object* collidee){

	//check if one is tait
	if (collider->getType() == TAIT || collidee->getType() ==TAIT){
		if(collider->getType() == BOMB || collidee->getType() == BOMB||
			collider->getType() == FROGMONSTER || collidee->getType() == FROGMONSTER ||
			collider->getType() == ANTMONSTER || collidee->getType() == ANTMONSTER)	{
			return true;
		}
	}
	return false;

}

//Searches the object assosiated with the floor in the params
Object* Game::findObject(Floor *loc){
	for(int i = 0; i < (int)objects.size(); i++){
		if(loc->getX() == objects[i]->getX() 
			&& loc->getY() == objects[i]->getY() 
			&& loc->getZ() == objects[i]->getZ())
			return objects[i];
	}
	return NULL; //this should never occur

}


//Checks to see if the collider is tait and if you're hitting any sort of object that can be counted
//as a Gate
bool Game::isGate(Object *collider, Object *collidee){
	ObjectType type1 = collider->getType();
	ObjectType type2 = collidee->getType();
	//add pick ups as we go
	return(type1 == TAIT 
		&& (type2 == REDGATE||type2 == GREENGATE||type2 == YELLOWGATE||type2 == ORANGEGATE||type2 == POTATOGATE));
}

//Attempt to go through gate
void Game::collideGate(Object *gate){
	ObjectType gateType = gate->getType();
	switch(gateType){
		case REDGATE:
			if(status->hasKey(RED)){
			removeObject(gate);
			status->removeKey(RED);
			}
			break;
		case ORANGEGATE:
			if(status->hasKey(ORANGE)){
			removeObject(gate);
			status->removeKey(ORANGE);
			};
			break;
		case GREENGATE:
			if(status->hasKey(GREEN)){
			removeObject(gate);
			status->removeKey(GREEN);
			}
			break;
		case YELLOWGATE:
			if(status->hasKey(YELLOW)){
			removeObject(gate);
			status->removeKey(YELLOW);
			}
			break;
		case POTATOGATE:
			if(status->potatosLeft()==0){
			removeObject(gate);
			}
			break;
	}
	
}

//Checks to see if the collider is tait and if you're hitting any sort of object that can be counted
//as a pick up, or exit
bool Game::isPickUp(Object *collider, Object *collidee){
	ObjectType type1 = collider->getType();
	ObjectType type2 = collidee->getType();
	//add pick ups as we go
	return(type1 == TAIT 
		&& (type2 == EXIT || type2 == SKATES||type2 == REDKEY||type2 == ORANGEKEY
		||type2 == GREENKEY || type2 == YELLOWKEY||type2 == POTATO||type2 == BOOTS));
}

//Add a pick up to inventory or win the game
void Game::collidePickUp(Object *item){
	ObjectType pickUp = item->getType();
	switch(pickUp){
		case EXIT:
			victory = true;
			break;
		case SKATES:
			status->pickUpItem(ICESKATES);
			break;
		case BOOTS:
			status->pickUpItem(FIREBOOTS);
			break;
		case POTATO:
			status->getPotato();
			break;
		case REDKEY:
			status->pickUpKey(RED);
			break;
		case GREENKEY:
			status->pickUpKey(GREEN);
			break;
		case ORANGEKEY:
			status->pickUpKey(ORANGE);
			break;
		case YELLOWKEY:
			status->pickUpKey(YELLOW);
			break;
	}
	removeObject(item);
}

//Cleans up used blocks and enemies
void Game::cleanUp(){
	for(int i =0; i< (int)objects.size();i++){
		if(objects[i]->needsRemoval()){
			objects.erase(objects.begin()+i);
			i--;
		}
	}

}

//Removes an Object from the objects vector
void Game::removeObject(Object *toRemove){
	for(int i =0; i< (int)objects.size();i++){
		if(objects[i] == toRemove){
			map[toRemove->getX()][toRemove->getY()][toRemove->getZ()]->changeObject(NONE);
			objects.erase(objects.begin()+i);
		}
	}

}




void Game::equipItem(Equip type) // FILTER FOR APPROPRIATE ITEMS
{
	status->equipItem(type);
}


vector<vector<vector<Floor*>>> Game::getMap(void)
{
	return map;
}

int Game::getMaxX(void)
{
	return maxX;
}

int Game::getMaxY(void)
{
	return maxY;
}

int Game::getMaxZ(void)
{
	return maxZ;
}

Status* Game::getStatus(){
	return status;
}